package com.atguigu.config;

import com.atguigu.aop.LogAspect;
import com.atguigu.aop.MathCalc;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * AOP:[动态代理]
 *      程序运行期间动态将某段代码切入到指定方法指定位置进行运行的编程方式
 *     1。导入aop pom--spring-aspects
 *     2.定义一个业务逻辑，在其运行的时候进行打印（运行前后，异常时等）
 *     3.定义一个日志切面类LogAspect 里面的方法需要感知监控的方法运行的环节
 *          通知方法：
 *              前置@Before：目标方法运行前通知
 *              后置@After：运行后通知
 *              返回@AfterReturning：正常返回之后通知
 *              异常@AfterThrowing：发生异常之后通知
 *              环绕@Around：动态代理，手动推进目标方法运行joinPoint.proceed
 *     4.将切面类和被切类都加入容器中
 *     5.切面类上加@Aspect
 *     6。配置类上@Enable
 *
 *  AOP原理：看给容器中注入了什么组件，这些组件的功能
 *  EnableAspectJAutoProxy
 *     1。import AspectJAutoProxyRegistrar给容器中注册bean的定义
 *      internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator
 *      给容器注册了一个AnnotationAwareAspectJAutoProxyCreator
 *     2。AnnotationAwareAspectJAutoProxyCreator
 *          ->AspectJAwareAdvisorAutoProxyCreator
 *              ->AbstractAdvisorAutoProxyCreator
 *                  ->AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
 *                  关注后置处理器、自动装配BeanFactory
 *
 *      AbstractAutoProxyCreator.setBeanFactory
 *      AbstractAutoProxyCreator.postProcessBeforeInstantiation
 *      AbstractAutoProxyCreator.postProcessAfterInstantiation
 *
 *      AbstractAdvisorAutoProxyCreator.setBeanFactory -> initBeanFactory
 *      AnnotationAwareAspectJAutoProxyCreator.setBeanFactory
 *
 *      AnnotationAwareAspectJAutoProxyCreator创建流程：
 *      1。传入配置类，创建ioc容器
 *      2。注册配置类，调用refresh
 *      3。registerBeanPostProcessors(beanFactory);注册bean的后置处理器方便拦截bean的创建
 *          1、先获取ioc容器中已经定义了的需要创建对象的BeanPostProcessor
 *          2、给容器中添加其他BeanPostProcessor（new BeanPostProcessorChecker）
 *          3、优先注册实现了PriorityOrdered接口的BeanPostProcessor
 *          4、再注册实现了Ordered接口的BeanPostProcessor
 *          5、再注册常规的没有实现Order接口的BeanPostProcessor
 *          6、registerBeanPostProcessor，实际上就是创建BeanPostProcessor，保存到容器中
 *              创建internalAutoProxyCreator的BeanPostProcessor（AnnotationAwareAspectJAutoProxyCreator
 *              1。创建bean实例
 *              2。populate属性赋值
 *              3。initializeBean初始化bean
 *                  1）invokeAwareMethods，处理Aware接口的方法回调
 *                  2）applyBeanPostProcessorsBeforeInitialization后置处理器的before调用
 *                  3）invokeInitMethods 执行自定义的初始化方法
 *                  4）applyBeanPostProcessorsAfterInitialization 执行后置处理器的after逻辑
 *              4。BeanPostProcessor（AnnotationAwareAspectJAutoProxyCreator）创建完成
 *          7、创建完成的BeanPostProcessor，添加到BeanFactory
 *             beanFactory.addBeanPostProcessor(postProcessor);
 ******************************************************************************************************
 *
 *       4。finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作，完成剩下的非lazy单例bean
 *          1、遍历获取容器中所有的bean，依次创建对象 getBean(beanName)
 *              getBean->doGetBean->getSingleton
 *          2、创建bean流程
 *          【结论：AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截】
 *              1）先从缓存中获取，如果能获取到代表之前已经创建过（只要创建好的bean都会被缓存起来）
 *              2）缓存中没有，createBean（）
 *                  a、resolveBeforeInstantiation希望在这里能返回一个代理对象，如果不能返回则继续步骤b
 *                      a.1 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
 *                          先拿到所有后置处理器，如果是InstantiationAwareBeanPostProcessor，
 *                          就执行
 *                          bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
 * 					        if (bean != null) {
 * 						    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
 *                          }
 *                  b、和3.6一样，创建一个bean实例
 *
 *BeanPostProcessor是在Bean对象创建完成初始化前后调用
 *InstantiationAwareBeanPostProcessor是在创建Bean实例之前尝试先用后置处理器返回对象
 *
 *
 * AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的作用
 * 1。每一个bean创建之前，调用postProcessBeforeInitialization（关心mathCalc和logAspect两个bean的拦截）
 *      1、判断当前bean是否在advisedBeans中（保存所有需要增强的bean）
 *      2、判断当前bean是否是基础类型的
 *              Advice.class.isAssignableFrom(beanClass) ||
 * 				Pointcut.class.isAssignableFrom(beanClass) ||
 * 				Advisor.class.isAssignableFrom(beanClass) ||
 * 				AopInfrastructureBean.class.isAssignableFrom(beanClass)
 * 		    或者是否是切面（写了Aspect注解
 * 		3、是否需要跳过
 * 	        a.获取候选的增强器（切面里的通知方法，包装了一下）
 * 	        判断每个增强器是否是AspectJPointcutAdvisor类型的，断点可知不是
 * 	        b。调用父类shouldskip，返回false
 *
 * 2。创建代理对象是哪里创建的嘞
 * postProcessAfterInitialization
 * return wrapIfNecessary(bean, beanName, cacheKey);
 *      1、获取当前bean所有的增强器
 *          找到候选所有增强器->获取能在当前bean使用的增强器->排序
 *      2、保存bean到advisedBean中
 *      3、如果当前bean需要增强，则创建其代理对象（Spring决定，实现接口的用jdk，没实现的用cglib）
 *      4、wrapIfNecessary方法给容器中返回增强过了的代理对象
 *      5、以后容器中获取bean就是获取的代理对象，执行目标方法时执行增强方法
 *
 * 3。目标方法执行
 * 容器中保存了组件的代理对象（CGLib增强对象），这个对象里面保存了详细的增强器和增强目标的信息
 *      1、CglibAopProxy.intercept()拦截目标方法的执行
 *      2、根据ProxyFactory对象获得将要执行目标方法的拦截器链
 *      3、没有拦截器链则直接执行目标方法
 *         有拦截器链，则retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
 *
 *
 */
@Configuration
@EnableAspectJAutoProxy
public class MainConfigAop {

    @Bean
    public MathCalc mathCalc() {
        return new MathCalc();
    }

    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }
}
